Predição de preços de Zagueiro lateral em R


# Setup Célula oculta para o setup:

Obtenção de Dados

Os dados são obtidos de um servidor SQL ou csv e armazenados na variável df

df <- read_csv("/media/njaneto/HD1/FIAP/PROGRAMANDO_IA_COM_R/fifa18-data-analysis/model/data/fifa18.csv", locale = locale(encoding = "ISO-8859-1"))
New names:
* `` -> ...1
Registered S3 method overwritten by 'cli':
  method     from
  print.tree tree
Rows: 17994 Columns: 58
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (9): name, full_name, club, league, nationality, Position, work_rate_att, work_rate_def, preferred_foot
dbl (49): ...1, ID, special, eur_value, eur_wage, eur_release_clause, crossing, finishing, heading_accuracy, short_...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
setDT(df)

Análise dos dados

Visualização inicial dos dados

Quantidade de linhas e colunas obtidas:

dim(df)
[1] 17994    58

Primeiros registros obtidos:

head(df)

Incluindo coluna de continente

Africa<-c('Algeria','Angola','Benin','Botswana','Burkina','Burundi','Cameroon','Cape Verde','Central African Republic','Chad','Comoros','Congo','Congo Democratic Republic of','Djibouti','Egypt','Equatorial Guinea','Eritrea','Ethiopia','Gabon','Gambia','Ghana','Guinea','Guinea-Bissau','Ivory Coast','Kenya','Lesotho','Liberia','Libya','Madagascar','Malawi','Mali','Mauritania','Mauritius','Morocco','Mozambique','Namibia','Niger','Nigeria','Rwanda','Sao Tome and Principe','Senegal','Seychelles','Sierra Leone','Somalia','South Africa','South Sudan','Sudan','Swaziland','Tanzania','Togo','Tunisia','Uganda','Zambia','Zimbabwe','Burkina Faso')
Antarctica<-c('Fiji','Kiribati','Marshall Islands','Micronesia','Nauru','New Zealand','Palau','Papua New Guinea','Samoa','Solomon Islands','Tonga','Tuvalu','Vanuatu')
Asia<-c('Afghanistan','Bahrain','Bangladesh','Bhutan','Brunei','Burma (Myanmar)','Cambodia','China','East Timor','India','Indonesia','Iran','Iraq','Israel','Japan','Jordan','Kazakhstan','North Korea','South Korea','Kuwait','Kyrgyzstan','Laos','Lebanon','Malaysia','Maldives','Mongolia','Nepal','Oman','Pakistan','Philippines','Qatar','Russian Federation','Saudi Arabia','Singapore','Sri Lanka','Syria','Tajikistan','Thailand','Turkey','Turkmenistan','United Arab Emirates','Uzbekistan','Vietnam','Yemen','Russia')
Europe<-c('Albania','Andorra','Armenia','Austria','Azerbaijan','Belarus','Belgium','Bosnia and Herzegovina','Bulgaria','Croatia','Cyprus','Czech Republic','Denmark','Estonia','Finland','France','Georgia','Germany','Greece','Hungary','Iceland','Ireland','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Macedonia','Malta','Moldova','Monaco','Montenegro','Netherlands','Norway','Poland','Portugal','Romania','San Marino','Scotland','Serbi','Slovakia','Slovenia','Spain','Sweden','Switzerland','Ukraine','England','Vatican City','Republic of Ireland','Wales')
North_america<-c('Antigua and Barbuda','Bahamas','Barbados','Belize','Canada','Costa Rica','Cuba','Dominica','Dominican Republic','El Salvador','Grenada','Guatemala','Haiti','Honduras','Jamaica','Mexico','Nicaragua','Panama','Saint Kitts and Nevis','Saint Lucia','Saint Vincent and the Grenadines','Trinidad and Tobago','United States')
South_america<-c('Argentina','Bolivia','Brazil','Chile','Colombia','Ecuador','Guyana','Paraguay','Peru','Suriname','Uruguay','Venezuela')

df[, continent:= df$nationality]
df <- df %>% relocate(continent, .after = nationality)

df$continent[df$continent %in% Africa ] <- "Africa"
df$continent[df$continent %in% Antarctica ] <- "Antarctica"
df$continent[df$continent %in% Asia ] <- "Asia"
df$continent[df$continent %in% Europe ] <- "Europe"
df$continent[df$continent %in% North_america ] <- "North_america"
df$continent[df$continent %in% South_america ] <- "South_america"

Graficos de analise dos dados

Validação incial de dados

plot_intro(df)

plot_missing(df)

Jogadores por pais

pais <- df[,.N, by=nationality]
fr <- joinCountryData2Map(dF=pais, joinCode = "NAME", nameJoinColumn = "nationality", verbose = F)
148 codes from your data successfully matched countries in the map
16 codes from your data failed to match with a country code in the map
95 codes from the map weren't represented in your data
mapCountryData(mapToPlot = fr,nameColumnToPlot = "N",catMethod = "fixedWidth",
               oceanCol = "steelblue1",missingCountryCol = "white",
               mapTitle = "Jogadores por pais",
               aspect = "variable") 
Warning in plot.window(xlim = xlim, ylim = ylim, asp = aspect) :
  NAs introduzidos por coerção

Filtrando somente atacantes

selecionando os atacantes

OBACK_EUROPE <- df %>%
  filter(Position == "Outside-back" & continent == "Europe")

head(OBACK_EUROPE)

selecionando os goleiros da variável

#-- base para validacao
OBACK_NOT_EUROPE <- df %>%
  filter(Position == "Outside-back" & continent != "Europe")

head(OBACK_NOT_EUROPE)

Removendo dados

removendo os NAs e variaves nao numericas da base de treino

OBACK_EUROPE <- OBACK_EUROPE %>% 
  select_if(~ !any(is.na(.))) %>%
  select_if(~ any(is.numeric(.)))

head(OBACK_EUROPE)

Removendo os NAs e variaves nao numericas da base de validacao

fifa.18.ob <- OBACK_NOT_EUROPE %>% 
  select_if(~ !any(is.na(.))) %>%
  select_if(~ any(is.numeric(.)))

head(fifa.18.ob)

Treinamento e predicao

Boxplot

Grafico de boxplot simples

boxplot(OBACK_EUROPE)

Correlacao

Grafico de Correlacao simples

corrMatrix <- cor(OBACK_EUROPE)
corrplot.mixed(corrMatrix, 
               lower = "ellipse", 
               upper = "number",
               tl.pos = "lt",
               tl.col = "black",
               order="hclust",
               hclust.method = "ward.D",
               addrect = 3)

Definição do modelo de ML

Modelo com Floresta

set.seed(1)
reg.test <- randomForest(formula = eur_value ~ ., 
                         data = OBACK_EUROPE, 
                         ntree=100, 
                         proximity=TRUE, 
                         localImp=TRUE)
plot(reg.test)

Predição

Avaliação

Predição dos preços dos goleiros

predito = predict(reg.test, OBACK_EUROPE)
print(paste("R2: ", R2_Score(predito, OBACK_EUROPE$eur_value) ) )
[1] "R2:  0.991296178162238"
print(paste("MSE: ", MSE(predito, OBACK_EUROPE$eur_value) ) )
[1] "MSE:  90733971182.37"
OBACK_EUROPE[, predito:=predito]
OBACK_EUROPE <- OBACK_EUROPE %>% relocate(predito, .after = eur_value)
head(OBACK_EUROPE)

Dispersão

Avaliaçao de acerto X erro do modelo

OBACK_EUROPE %>%
  mutate(predito = predict(reg.test, .)) %>%
  plot_ly(x = ~eur_value,
          y= ~predito,
          type='scatter',
          mode='markers',
          text=~paste0("Real value: ", currency(eur_value, symbol='€', digits = 0L), 
                       "\nPredicted value: ", currency(predito, symbol='€', digits = 0L), 
                       "\nError: ", (eur_value - predito)),
          name="Dispersão") %>%
  add_segments(x=0, y=0, xend = 100000000, yend = 100000000, name="Equilíbrio")

Avaliação II

Avaliação dos preços (baseado nos dados que nunca viu antes!)

Predição dos preços dos goleiros

predito = predict(reg.test, fifa.18.ob)
print(paste("R2: ", R2_Score(predito, fifa.18.ob$eur_value) ) )
[1] "R2:  0.883693708450125"
print(paste("MSE: ", MSE(predito, fifa.18.ob$eur_value) ) )
[1] "MSE:  932950885137.678"
fifa.18.ob[, predito:=predito]
fifa.18.ob <- fifa.18.ob %>% relocate(predito, .after = eur_value)
head(fifa.18.ob)

Dispersão

fifa.18.ob %>%
  mutate(predito = predict(reg.test, .)) %>%
  plot_ly(x = ~eur_value,
          y= ~predito,
          type='scatter',
          mode='markers',
          text=~paste0("Real value: ", currency(eur_value, symbol='€', digits = 0L), 
                       "\nPredicted value: ", currency(predito, symbol='€', digits = 0L), 
                       "\nError: ", (eur_value - predito)),
          name="Dispersão") %>%
  add_segments(x=0, y=0, xend = 100000000, yend = 100000000, name="Equilíbrio")

Resultado

resultado final com os goleiros e seus valores preditos

output <- OBACK_NOT_EUROPE %>%
  select(Position, name, eur_value) 

output[, eur_value := currency(fifa.18.ob$eur_value, symbol = '€', digits = 0L)]
output[, 'Preço "Calculado" (€)' := currency(fifa.18.ob$predito, symbol = '€', digits = 0L)]
output[, 'Potencial Valorização (€)' := currency((fifa.18.ob$predito - fifa.18.ob$eur_value), symbol='€', digits = 0L) ]
output[, 'Potencial Valorização (%)' := (percent((fifa.18.ob$predito - fifa.18.ob$eur_value) / 100000000)) ]

output <- output %>% 
  rename(
    'Posição' = Position,
    'Jogador' = name,
    'Preço de mercado' = eur_value
  )

head(output)
NA

Rodapé

Case de Advanced Analytics


Pedro Albuquerque - São Paulo - 2021

LS0tCnRpdGxlOiAiQ2FzZSBkZSBBZHZhbmNlZCBBbmFseXRpY3MgIgphdXRob3I6ICJQZWRybyBBbGJ1cXVlcnF1ZSIKc3VidGl0bGU6ICdBbmFsaXNlIHBhcmEgZGV0ZXJtaW5hciBvcyBaYWd1ZWlybyBsYXRlcmFsIGNvbSBtYWlvciBwb3RlbmNpYWwgY3VzdG8tYmVuZWZpY2lvICcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGZpZ193aWR0aDogMTAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KIyBQcmVkacOnw6NvIGRlIHByZcOnb3MgZGUgWmFndWVpcm8gbGF0ZXJhbCBlbSBSCiFbXShpbWcvWmFndWVpcm9fbGF0ZXJhbC5qcGcpe3dpZHRoPTEwMCV9ICAKIyBTZXR1cApDw6lsdWxhIG9jdWx0YSBwYXJhIG8gc2V0dXA6CmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpybShsaXN0PWxzKCkpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeSh0cmVlKQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShNTG1ldHJpY3MpCmxpYnJhcnkoRFQpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShmb3JtYXR0YWJsZSkKI2xpYnJhcnkoUlBvc3RncmVzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KERhdGFFeHBsb3JlcikKbGlicmFyeShyd29ybGRtYXApCm9wdGlvbnMoc2NpcGVuID0gOTk5LCBkaWdpdHMgPSA0KQpgYGAKCiMgT2J0ZW7Dp8OjbyBkZSBEYWRvcwpPcyBkYWRvcyBzw6NvIG9idGlkb3MgZGUgdW0gc2Vydmlkb3IgU1FMIG91IGNzdiBlIGFybWF6ZW5hZG9zIG5hIHZhcmnDoXZlbCBgZGZgCgpgYGB7cn0KZGYgPC0gcmVhZF9jc3YoIi9tZWRpYS9uamFuZXRvL0hEMS9GSUFQL1BST0dSQU1BTkRPX0lBX0NPTV9SL2ZpZmExOC1kYXRhLWFuYWx5c2lzL21vZGVsL2RhdGEvZmlmYTE4LmNzdiIsIGxvY2FsZSA9IGxvY2FsZShlbmNvZGluZyA9ICJJU08tODg1OS0xIikpCnNldERUKGRmKQpgYGAKIyBBbsOhbGlzZSBkb3MgZGFkb3MKCiMjIFZpc3VhbGl6YcOnw6NvIGluaWNpYWwgZG9zIGRhZG9zClF1YW50aWRhZGUgZGUgbGluaGFzIGUgY29sdW5hcyBvYnRpZGFzOgpgYGB7cn0KZGltKGRmKQpgYGAKUHJpbWVpcm9zIHJlZ2lzdHJvcyBvYnRpZG9zOgpgYGB7cn0KaGVhZChkZikKYGBgCkluY2x1aW5kbyBjb2x1bmEgZGUgY29udGluZW50ZQpgYGB7cn0KQWZyaWNhPC1jKCdBbGdlcmlhJywnQW5nb2xhJywnQmVuaW4nLCdCb3Rzd2FuYScsJ0J1cmtpbmEnLCdCdXJ1bmRpJywnQ2FtZXJvb24nLCdDYXBlIFZlcmRlJywnQ2VudHJhbCBBZnJpY2FuIFJlcHVibGljJywnQ2hhZCcsJ0NvbW9yb3MnLCdDb25nbycsJ0NvbmdvIERlbW9jcmF0aWMgUmVwdWJsaWMgb2YnLCdEamlib3V0aScsJ0VneXB0JywnRXF1YXRvcmlhbCBHdWluZWEnLCdFcml0cmVhJywnRXRoaW9waWEnLCdHYWJvbicsJ0dhbWJpYScsJ0doYW5hJywnR3VpbmVhJywnR3VpbmVhLUJpc3NhdScsJ0l2b3J5IENvYXN0JywnS2VueWEnLCdMZXNvdGhvJywnTGliZXJpYScsJ0xpYnlhJywnTWFkYWdhc2NhcicsJ01hbGF3aScsJ01hbGknLCdNYXVyaXRhbmlhJywnTWF1cml0aXVzJywnTW9yb2NjbycsJ01vemFtYmlxdWUnLCdOYW1pYmlhJywnTmlnZXInLCdOaWdlcmlhJywnUndhbmRhJywnU2FvIFRvbWUgYW5kIFByaW5jaXBlJywnU2VuZWdhbCcsJ1NleWNoZWxsZXMnLCdTaWVycmEgTGVvbmUnLCdTb21hbGlhJywnU291dGggQWZyaWNhJywnU291dGggU3VkYW4nLCdTdWRhbicsJ1N3YXppbGFuZCcsJ1RhbnphbmlhJywnVG9nbycsJ1R1bmlzaWEnLCdVZ2FuZGEnLCdaYW1iaWEnLCdaaW1iYWJ3ZScsJ0J1cmtpbmEgRmFzbycpCkFudGFyY3RpY2E8LWMoJ0ZpamknLCdLaXJpYmF0aScsJ01hcnNoYWxsIElzbGFuZHMnLCdNaWNyb25lc2lhJywnTmF1cnUnLCdOZXcgWmVhbGFuZCcsJ1BhbGF1JywnUGFwdWEgTmV3IEd1aW5lYScsJ1NhbW9hJywnU29sb21vbiBJc2xhbmRzJywnVG9uZ2EnLCdUdXZhbHUnLCdWYW51YXR1JykKQXNpYTwtYygnQWZnaGFuaXN0YW4nLCdCYWhyYWluJywnQmFuZ2xhZGVzaCcsJ0JodXRhbicsJ0JydW5laScsJ0J1cm1hIChNeWFubWFyKScsJ0NhbWJvZGlhJywnQ2hpbmEnLCdFYXN0IFRpbW9yJywnSW5kaWEnLCdJbmRvbmVzaWEnLCdJcmFuJywnSXJhcScsJ0lzcmFlbCcsJ0phcGFuJywnSm9yZGFuJywnS2F6YWtoc3RhbicsJ05vcnRoIEtvcmVhJywnU291dGggS29yZWEnLCdLdXdhaXQnLCdLeXJneXpzdGFuJywnTGFvcycsJ0xlYmFub24nLCdNYWxheXNpYScsJ01hbGRpdmVzJywnTW9uZ29saWEnLCdOZXBhbCcsJ09tYW4nLCdQYWtpc3RhbicsJ1BoaWxpcHBpbmVzJywnUWF0YXInLCdSdXNzaWFuIEZlZGVyYXRpb24nLCdTYXVkaSBBcmFiaWEnLCdTaW5nYXBvcmUnLCdTcmkgTGFua2EnLCdTeXJpYScsJ1RhamlraXN0YW4nLCdUaGFpbGFuZCcsJ1R1cmtleScsJ1R1cmttZW5pc3RhbicsJ1VuaXRlZCBBcmFiIEVtaXJhdGVzJywnVXpiZWtpc3RhbicsJ1ZpZXRuYW0nLCdZZW1lbicsJ1J1c3NpYScpCkV1cm9wZTwtYygnQWxiYW5pYScsJ0FuZG9ycmEnLCdBcm1lbmlhJywnQXVzdHJpYScsJ0F6ZXJiYWlqYW4nLCdCZWxhcnVzJywnQmVsZ2l1bScsJ0Jvc25pYSBhbmQgSGVyemVnb3ZpbmEnLCdCdWxnYXJpYScsJ0Nyb2F0aWEnLCdDeXBydXMnLCdDemVjaCBSZXB1YmxpYycsJ0Rlbm1hcmsnLCdFc3RvbmlhJywnRmlubGFuZCcsJ0ZyYW5jZScsJ0dlb3JnaWEnLCdHZXJtYW55JywnR3JlZWNlJywnSHVuZ2FyeScsJ0ljZWxhbmQnLCdJcmVsYW5kJywnSXRhbHknLCdMYXR2aWEnLCdMaWVjaHRlbnN0ZWluJywnTGl0aHVhbmlhJywnTHV4ZW1ib3VyZycsJ01hY2Vkb25pYScsJ01hbHRhJywnTW9sZG92YScsJ01vbmFjbycsJ01vbnRlbmVncm8nLCdOZXRoZXJsYW5kcycsJ05vcndheScsJ1BvbGFuZCcsJ1BvcnR1Z2FsJywnUm9tYW5pYScsJ1NhbiBNYXJpbm8nLCdTY290bGFuZCcsJ1NlcmJpJywnU2xvdmFraWEnLCdTbG92ZW5pYScsJ1NwYWluJywnU3dlZGVuJywnU3dpdHplcmxhbmQnLCdVa3JhaW5lJywnRW5nbGFuZCcsJ1ZhdGljYW4gQ2l0eScsJ1JlcHVibGljIG9mIElyZWxhbmQnLCdXYWxlcycpCk5vcnRoX2FtZXJpY2E8LWMoJ0FudGlndWEgYW5kIEJhcmJ1ZGEnLCdCYWhhbWFzJywnQmFyYmFkb3MnLCdCZWxpemUnLCdDYW5hZGEnLCdDb3N0YSBSaWNhJywnQ3ViYScsJ0RvbWluaWNhJywnRG9taW5pY2FuIFJlcHVibGljJywnRWwgU2FsdmFkb3InLCdHcmVuYWRhJywnR3VhdGVtYWxhJywnSGFpdGknLCdIb25kdXJhcycsJ0phbWFpY2EnLCdNZXhpY28nLCdOaWNhcmFndWEnLCdQYW5hbWEnLCdTYWludCBLaXR0cyBhbmQgTmV2aXMnLCdTYWludCBMdWNpYScsJ1NhaW50IFZpbmNlbnQgYW5kIHRoZSBHcmVuYWRpbmVzJywnVHJpbmlkYWQgYW5kIFRvYmFnbycsJ1VuaXRlZCBTdGF0ZXMnKQpTb3V0aF9hbWVyaWNhPC1jKCdBcmdlbnRpbmEnLCdCb2xpdmlhJywnQnJhemlsJywnQ2hpbGUnLCdDb2xvbWJpYScsJ0VjdWFkb3InLCdHdXlhbmEnLCdQYXJhZ3VheScsJ1BlcnUnLCdTdXJpbmFtZScsJ1VydWd1YXknLCdWZW5lenVlbGEnKQoKZGZbLCBjb250aW5lbnQ6PSBkZiRuYXRpb25hbGl0eV0KZGYgPC0gZGYgJT4lIHJlbG9jYXRlKGNvbnRpbmVudCwgLmFmdGVyID0gbmF0aW9uYWxpdHkpCgpkZiRjb250aW5lbnRbZGYkY29udGluZW50ICVpbiUgQWZyaWNhIF0gPC0gIkFmcmljYSIKZGYkY29udGluZW50W2RmJGNvbnRpbmVudCAlaW4lIEFudGFyY3RpY2EgXSA8LSAiQW50YXJjdGljYSIKZGYkY29udGluZW50W2RmJGNvbnRpbmVudCAlaW4lIEFzaWEgXSA8LSAiQXNpYSIKZGYkY29udGluZW50W2RmJGNvbnRpbmVudCAlaW4lIEV1cm9wZSBdIDwtICJFdXJvcGUiCmRmJGNvbnRpbmVudFtkZiRjb250aW5lbnQgJWluJSBOb3J0aF9hbWVyaWNhIF0gPC0gIk5vcnRoX2FtZXJpY2EiCmRmJGNvbnRpbmVudFtkZiRjb250aW5lbnQgJWluJSBTb3V0aF9hbWVyaWNhIF0gPC0gIlNvdXRoX2FtZXJpY2EiCgpgYGAKIyMgR3JhZmljb3MgZGUgYW5hbGlzZSBkb3MgZGFkb3MKVmFsaWRhw6fDo28gaW5jaWFsIGRlIGRhZG9zIApgYGB7cn0KcGxvdF9pbnRybyhkZikKYGBgCgoKYGBge3J9CnBsb3RfbWlzc2luZyhkZikKYGBgCgpKb2dhZG9yZXMgcG9yIHBhaXMKYGBge3J9CnBhaXMgPC0gZGZbLC5OLCBieT1uYXRpb25hbGl0eV0KZnIgPC0gam9pbkNvdW50cnlEYXRhMk1hcChkRj1wYWlzLCBqb2luQ29kZSA9ICJOQU1FIiwgbmFtZUpvaW5Db2x1bW4gPSAibmF0aW9uYWxpdHkiLCB2ZXJib3NlID0gRikKCm1hcENvdW50cnlEYXRhKG1hcFRvUGxvdCA9IGZyLG5hbWVDb2x1bW5Ub1Bsb3QgPSAiTiIsY2F0TWV0aG9kID0gImZpeGVkV2lkdGgiLAogICAgICAgICAgICAgICBvY2VhbkNvbCA9ICJzdGVlbGJsdWUxIixtaXNzaW5nQ291bnRyeUNvbCA9ICJ3aGl0ZSIsCiAgICAgICAgICAgICAgIG1hcFRpdGxlID0gIkpvZ2Fkb3JlcyBwb3IgcGFpcyIsCiAgICAgICAgICAgICAgIGFzcGVjdCA9ICJ2YXJpYWJsZSIpIAoKYGBgCgoKIyMgRmlsdHJhbmRvIHNvbWVudGUgYXRhY2FudGVzCnNlbGVjaW9uYW5kbyBvcyBhdGFjYW50ZXMgCmBgYHtyfQpPQkFDS19FVVJPUEUgPC0gZGYgJT4lCiAgZmlsdGVyKFBvc2l0aW9uID09ICJPdXRzaWRlLWJhY2siICYgY29udGluZW50ID09ICJFdXJvcGUiKQoKaGVhZChPQkFDS19FVVJPUEUpCmBgYApzZWxlY2lvbmFuZG8gb3MgZ29sZWlyb3MgZGEgdmFyacOhdmVsIApgYGB7cn0KIy0tIGJhc2UgcGFyYSB2YWxpZGFjYW8KT0JBQ0tfTk9UX0VVUk9QRSA8LSBkZiAlPiUKICBmaWx0ZXIoUG9zaXRpb24gPT0gIk91dHNpZGUtYmFjayIgJiBjb250aW5lbnQgIT0gIkV1cm9wZSIpCgpoZWFkKE9CQUNLX05PVF9FVVJPUEUpCmBgYAoKIyMgUmVtb3ZlbmRvIGRhZG9zIApyZW1vdmVuZG8gb3MgTkFzIGUgdmFyaWF2ZXMgbmFvIG51bWVyaWNhcyBkYSBiYXNlIGRlIHRyZWlubwpgYGB7cn0KT0JBQ0tfRVVST1BFIDwtIE9CQUNLX0VVUk9QRSAlPiUgCiAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpICU+JQogIHNlbGVjdF9pZih+IGFueShpcy5udW1lcmljKC4pKSkKCmhlYWQoT0JBQ0tfRVVST1BFKQpgYGAKUmVtb3ZlbmRvIG9zIE5BcyBlIHZhcmlhdmVzIG5hbyBudW1lcmljYXMgZGEgYmFzZSBkZSB2YWxpZGFjYW8KYGBge3J9CmZpZmEuMTgub2IgPC0gT0JBQ0tfTk9UX0VVUk9QRSAlPiUgCiAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpICU+JQogIHNlbGVjdF9pZih+IGFueShpcy5udW1lcmljKC4pKSkKCmhlYWQoZmlmYS4xOC5vYikKYGBgCgojICBUcmVpbmFtZW50byBlIHByZWRpY2FvCgojIyBCb3hwbG90IApHcmFmaWNvIGRlIGJveHBsb3Qgc2ltcGxlcwpgYGB7cn0KYm94cGxvdChPQkFDS19FVVJPUEUpCmBgYAoKIyMgQ29ycmVsYWNhbyAKCkdyYWZpY28gZGUgQ29ycmVsYWNhbyBzaW1wbGVzCmBgYHtyfQpjb3JyTWF0cml4IDwtIGNvcihPQkFDS19FVVJPUEUpCmNvcnJwbG90Lm1peGVkKGNvcnJNYXRyaXgsIAogICAgICAgICAgICAgICBsb3dlciA9ICJlbGxpcHNlIiwgCiAgICAgICAgICAgICAgIHVwcGVyID0gIm51bWJlciIsCiAgICAgICAgICAgICAgIHRsLnBvcyA9ICJsdCIsCiAgICAgICAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsCiAgICAgICAgICAgICAgIG9yZGVyPSJoY2x1c3QiLAogICAgICAgICAgICAgICBoY2x1c3QubWV0aG9kID0gIndhcmQuRCIsCiAgICAgICAgICAgICAgIGFkZHJlY3QgPSAzKQoKYGBgCiMjIERlZmluacOnw6NvIGRvIG1vZGVsbyBkZSBNTCAKCk1vZGVsbyBjb20gRmxvcmVzdGEKYGBge3J9CnNldC5zZWVkKDEpCnJlZy50ZXN0IDwtIHJhbmRvbUZvcmVzdChmb3JtdWxhID0gZXVyX3ZhbHVlIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gT0JBQ0tfRVVST1BFLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG50cmVlPTEwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBwcm94aW1pdHk9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhbEltcD1UUlVFKQpwbG90KHJlZy50ZXN0KQoKYGBgCiMgUHJlZGnDp8OjbwojIyBBdmFsaWHDp8OjbyAgClByZWRpw6fDo28gZG9zIHByZcOnb3MgZG9zIGdvbGVpcm9zCmBgYHtyfQpwcmVkaXRvID0gcHJlZGljdChyZWcudGVzdCwgT0JBQ0tfRVVST1BFKQpwcmludChwYXN0ZSgiUjI6ICIsIFIyX1Njb3JlKHByZWRpdG8sIE9CQUNLX0VVUk9QRSRldXJfdmFsdWUpICkgKQpwcmludChwYXN0ZSgiTVNFOiAiLCBNU0UocHJlZGl0bywgT0JBQ0tfRVVST1BFJGV1cl92YWx1ZSkgKSApCmBgYApgYGB7cn0KT0JBQ0tfRVVST1BFWywgcHJlZGl0bzo9cHJlZGl0b10KT0JBQ0tfRVVST1BFIDwtIE9CQUNLX0VVUk9QRSAlPiUgcmVsb2NhdGUocHJlZGl0bywgLmFmdGVyID0gZXVyX3ZhbHVlKQpoZWFkKE9CQUNLX0VVUk9QRSkKYGBgCiMjIERpc3BlcnPDo28KQXZhbGlhw6dhbyBkZSBhY2VydG8gWCBlcnJvIGRvIG1vZGVsbyAKCmBgYHtyfQpPQkFDS19FVVJPUEUgJT4lCiAgbXV0YXRlKHByZWRpdG8gPSBwcmVkaWN0KHJlZy50ZXN0LCAuKSkgJT4lCiAgcGxvdF9seSh4ID0gfmV1cl92YWx1ZSwKICAgICAgICAgIHk9IH5wcmVkaXRvLAogICAgICAgICAgdHlwZT0nc2NhdHRlcicsCiAgICAgICAgICBtb2RlPSdtYXJrZXJzJywKICAgICAgICAgIHRleHQ9fnBhc3RlMCgiUmVhbCB2YWx1ZTogIiwgY3VycmVuY3koZXVyX3ZhbHVlLCBzeW1ib2w9J+KCrCcsIGRpZ2l0cyA9IDBMKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIlxuUHJlZGljdGVkIHZhbHVlOiAiLCBjdXJyZW5jeShwcmVkaXRvLCBzeW1ib2w9J+KCrCcsIGRpZ2l0cyA9IDBMKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIlxuRXJyb3I6ICIsIChldXJfdmFsdWUgLSBwcmVkaXRvKSksCiAgICAgICAgICBuYW1lPSJEaXNwZXJzw6NvIikgJT4lCiAgYWRkX3NlZ21lbnRzKHg9MCwgeT0wLCB4ZW5kID0gMTAwMDAwMDAwLCB5ZW5kID0gMTAwMDAwMDAwLCBuYW1lPSJFcXVpbMOtYnJpbyIpCmBgYAoKIyMgQXZhbGlhw6fDo28gSUkKIyMjIEF2YWxpYcOnw6NvIGRvcyBwcmXDp29zIChiYXNlYWRvIG5vcyBkYWRvcyBxdWUgbnVuY2Egdml1IGFudGVzISkKUHJlZGnDp8OjbyBkb3MgcHJlw6dvcyBkb3MgZ29sZWlyb3MKYGBge3J9CnByZWRpdG8gPSBwcmVkaWN0KHJlZy50ZXN0LCBmaWZhLjE4Lm9iKQpwcmludChwYXN0ZSgiUjI6ICIsIFIyX1Njb3JlKHByZWRpdG8sIGZpZmEuMTgub2IkZXVyX3ZhbHVlKSApICkKcHJpbnQocGFzdGUoIk1TRTogIiwgTVNFKHByZWRpdG8sIGZpZmEuMTgub2IkZXVyX3ZhbHVlKSApICkKYGBgCmBgYHtyfQpmaWZhLjE4Lm9iWywgcHJlZGl0bzo9cHJlZGl0b10KZmlmYS4xOC5vYiA8LSBmaWZhLjE4Lm9iICU+JSByZWxvY2F0ZShwcmVkaXRvLCAuYWZ0ZXIgPSBldXJfdmFsdWUpCmhlYWQoZmlmYS4xOC5vYikKYGBgCiMjIERpc3BlcnPDo28KYGBge3J9CmZpZmEuMTgub2IgJT4lCiAgbXV0YXRlKHByZWRpdG8gPSBwcmVkaWN0KHJlZy50ZXN0LCAuKSkgJT4lCiAgcGxvdF9seSh4ID0gfmV1cl92YWx1ZSwKICAgICAgICAgIHk9IH5wcmVkaXRvLAogICAgICAgICAgdHlwZT0nc2NhdHRlcicsCiAgICAgICAgICBtb2RlPSdtYXJrZXJzJywKICAgICAgICAgIHRleHQ9fnBhc3RlMCgiUmVhbCB2YWx1ZTogIiwgY3VycmVuY3koZXVyX3ZhbHVlLCBzeW1ib2w9J+KCrCcsIGRpZ2l0cyA9IDBMKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIlxuUHJlZGljdGVkIHZhbHVlOiAiLCBjdXJyZW5jeShwcmVkaXRvLCBzeW1ib2w9J+KCrCcsIGRpZ2l0cyA9IDBMKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIlxuRXJyb3I6ICIsIChldXJfdmFsdWUgLSBwcmVkaXRvKSksCiAgICAgICAgICBuYW1lPSJEaXNwZXJzw6NvIikgJT4lCiAgYWRkX3NlZ21lbnRzKHg9MCwgeT0wLCB4ZW5kID0gMTAwMDAwMDAwLCB5ZW5kID0gMTAwMDAwMDAwLCBuYW1lPSJFcXVpbMOtYnJpbyIpCmBgYAoKIyAgUmVzdWx0YWRvCgojIyByZXN1bHRhZG8gZmluYWwgY29tIG9zIGdvbGVpcm9zIGUgc2V1cyB2YWxvcmVzIHByZWRpdG9zIAoKYGBge3J9Cm91dHB1dCA8LSBPQkFDS19OT1RfRVVST1BFICU+JQogIHNlbGVjdChQb3NpdGlvbiwgbmFtZSwgZXVyX3ZhbHVlKSAKCm91dHB1dFssIGV1cl92YWx1ZSA6PSBjdXJyZW5jeShmaWZhLjE4Lm9iJGV1cl92YWx1ZSwgc3ltYm9sID0gJ+KCrCcsIGRpZ2l0cyA9IDBMKV0Kb3V0cHV0WywgJ1ByZcOnbyAiQ2FsY3VsYWRvIiAo4oKsKScgOj0gY3VycmVuY3koZmlmYS4xOC5vYiRwcmVkaXRvLCBzeW1ib2wgPSAn4oKsJywgZGlnaXRzID0gMEwpXQpvdXRwdXRbLCAnUG90ZW5jaWFsIFZhbG9yaXphw6fDo28gKOKCrCknIDo9IGN1cnJlbmN5KChmaWZhLjE4Lm9iJHByZWRpdG8gLSBmaWZhLjE4Lm9iJGV1cl92YWx1ZSksIHN5bWJvbD0n4oKsJywgZGlnaXRzID0gMEwpIF0Kb3V0cHV0WywgJ1BvdGVuY2lhbCBWYWxvcml6YcOnw6NvICglKScgOj0gKHBlcmNlbnQoKGZpZmEuMTgub2IkcHJlZGl0byAtIGZpZmEuMTgub2IkZXVyX3ZhbHVlKSAvIDEwMDAwMDAwMCkpIF0KCm91dHB1dCA8LSBvdXRwdXQgJT4lIAogIHJlbmFtZSgKICAgICdQb3Npw6fDo28nID0gUG9zaXRpb24sCiAgICAnSm9nYWRvcicgPSBuYW1lLAogICAgJ1ByZcOnbyBkZSBtZXJjYWRvJyA9IGV1cl92YWx1ZQogICkKCmhlYWQob3V0cHV0KQoKYGBgCioqKioqCiMgUm9kYXDDqQojIyMgQ2FzZSBkZSBBZHZhbmNlZCBBbmFseXRpY3MKKioqKioKKlBlZHJvIEFsYnVxdWVycXVlIC0gU8OjbyBQYXVsbyAtIDIwMjEq